/*//////////////////////////////////////////////////////////////////////////////
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2005-2007 Intel Corporation. All Rights Reserved.
//
*/

#include "umc_defs.h"

#if defined (UMC_ENABLE_AAC_INT_AUDIO_DECODER)

#include <ipps.h>
#include <math.h>
#include "sbr_settings.h"
#include "sbr_dec_api_int.h"
#include "sbr_dec_own_int.h"

/********************************************************************/

#ifndef ID_SCE
#define ID_SCE    0x0
#endif

#ifndef ID_CPE
#define ID_CPE    0x1
#endif

/********************************************************************/

static Ipp32s SBRI_TABLE_DEQUANT_ENV[2] = { 0x20000000, 0x2D413CCC };

/********************************************************************/

/* SBRI_TABLE_DEQUANT_INVERT[i] = 2^1 / (1 + 2^(12 - i)), Q30 */
static const Ipp32s SBRI_TABLE_DEQUANT_INVERT[25] = {
  0x0007ff80, 0x000ffe00, 0x001ff802, 0x003fe010,
  0x007f8080, 0x00fe03f8, 0x01f81f82, 0x03e0f83e,
  0x07878788, 0x0e38e38e, 0x1999999a, 0x2aaaaaab,
  0x40000000, 0x55555555, 0x66666666, 0x71c71c72,
  0x78787878, 0x7c1f07c2, 0x7e07e07e, 0x7f01fc08,
  0x7f807f80, 0x7fc01ff0, 0x7fe007fe, 0x7ff00200,
  0x7ff80080,
};

/********************************************************************/

Ipp32s sbriDequantInvertCouple(Ipp32s *pSrcDstL, Ipp16s *pSrcR, Ipp32s *pDstR,
                               Ipp32s len, Ipp32s shift)
{
  Ipp32s  expR, expL;
  Ipp32s  i;

  for (i = 0; i < len; i++) {
    expL = pSrcDstL[i];
    expR = pSrcR[i] >> shift;
/*
 * clip expR [0, 24]
 */
    if (expR < 0)
      expR = 0;
    if (expR > 24)
      expR = 24;

    pDstR[i] =
      MUL32_SBR_32S(expL, SBRI_TABLE_DEQUANT_INVERT[24 - expR]) << 2;
    pSrcDstL[i] =
      MUL32_SBR_32S(expL, SBRI_TABLE_DEQUANT_INVERT[expR]) << 2;
  }

  return 0;     // OK
}

/********************************************************************/

Ipp32s sbriDequantNoiseUncouple(Ipp32s *pSrcDstL, Ipp16s *pSrcR, Ipp32s *pDstR,
                                Ipp32s len)
{

  sbriDequantInvertCouple(pSrcDstL, pSrcR, pDstR, len, 0);

  return 0;     // OK
}

/********************************************************************/

Ipp32s sbriDequantEnvUncouple(Ipp32s *pSrcDstEnvL, Ipp16s *pSrcEnvR,
                              Ipp32s *pDstEnvR, Ipp32s numEnv, Ipp32s *vecSize,
                              Ipp32s ampRes)
{
  Ipp32s  len;
  Ipp32s  env;
  Ipp32s  shift = 1 - ampRes;
  Ipp32s *pBufL = pSrcDstEnvL;
  Ipp16s *pBufInR = pSrcEnvR;
  Ipp32s *pBufOutR = pDstEnvR;

  for (env = 0; env < numEnv; env++) {
    len = vecSize[env + 1] - vecSize[env];

    sbriDequantInvertCouple(pBufL, pBufInR, pBufOutR, len, shift);

    pBufL += len;
    pBufInR += len;
    pBufOutR += len;
  }

  return 0;     // OK
}

/********************************************************************/

Ipp32s sbriDequantNoiseMono(Ipp16s *pSrc, Ipp32s *pDst, Ipp32s len)
{
  Ipp32s  i;
  Ipp32s  currScaleFactor;

  if (len <= 0)
    return 0;

  for (i = 0; i < len; i++) {
    currScaleFactor = SBR_DEQUANT_OFFSET - pSrc[i] + 24;        /* 2^6 *
                                                                 * 2^(-exp) *
                                                                 * Q24 */

    if (currScaleFactor < 0)
      pDst[i] = 0;

    else if (pSrc[i] < 30)
      pDst[i] = 1 << currScaleFactor;

    else
      pDst[i] = 0x3fffffff;
  }

  return 0;
}

/********************************************************************/

Ipp32s sbriDequantEnvMono_OneBand(Ipp16s *pSrc, Ipp32s *pDst, Ipp32s len, Ipp32s ampRes)
{
  Ipp16s  maxScaleFactor, currScaleFactor;
  Ipp32s  i;
  Ipp32s  shift = 1 - ampRes;

  if (len <= 0)
    return 0;

  ippsMax_16s(pSrc, len, &maxScaleFactor);
  maxScaleFactor >>= shift;

  for (i = 0; i < len; i++) {
// currScaleFactor = maxScaleFactor - (pSrc[i] >> shift) ;
    currScaleFactor = (Ipp16s)IPP_MIN(maxScaleFactor - (pSrc[i] >> shift), 31);
    pDst[i] = SBRI_TABLE_DEQUANT_ENV[pSrc[i] & shift & 0x1] >> currScaleFactor;
  }

  return 29 - (6 + maxScaleFactor);
}

/********************************************************************/

static Ipp32s sbrDequantEnvMono(Ipp16s *pSrc, Ipp32s *pDst, Ipp32s *vecSize, Ipp32s LE,
                                Ipp32s bs_amp_res, Ipp32s *vecOffset)
{
  Ipp32s  l, len;
  Ipp16s *pBufSrc = pSrc;
  Ipp32s *pBufDst = pDst;

  for (l = 0; l < LE; l++) {

    len = vecSize[l + 1] - vecSize[l];

    vecOffset[l] = sbriDequantEnvMono_OneBand(pBufSrc, pBufDst, len, bs_amp_res);

    pBufSrc += len;
    pBufDst += len;
  }

  return 0;     // OK
}

/********************************************************************/

Ipp32s sbriDequantization(sSBRDecComState * pSbrCom, sSBRDecWorkState * pSbrWS,
                          Ipp32s *sfsEnv, Ipp32s ch, Ipp32s bs_amp_res)
{
  Ipp32s  error = 0;    // OK
  sSBREnvDataState* pEDState = &(pSbrCom->sbrEDState[ch]);
  sSBREnvDataState* pEDState0 = &(pSbrCom->sbrEDState[0]);
  sSBREnvDataState* pEDState1 = &(pSbrCom->sbrEDState[1]);

/* ORDINARY */
/*
 * envelope
 */
  sbrDequantEnvMono(pEDState->bufEnvQuant, pSbrWS->bufEnvOrig[ch],
                    pEDState->vSizeEnv, pSbrCom->sbrFIState[ch].nEnv, bs_amp_res,
                    sfsEnv);

/*
 * noise
 */
  sbriDequantNoiseMono(pEDState->bufNoiseQuant, pSbrWS->bufNoiseOrig[ch],
                       pEDState->vSizeNoise[pSbrCom->sbrFIState[ch].nNoiseEnv]);

/* COUPLE MODE */
  if (pSbrCom->bs_coupling == 1) {

/*
 * envelope
 */
    sbriDequantEnvUncouple(pSbrWS->bufEnvOrig[0], pEDState1->bufEnvQuant,
                           pSbrWS->bufEnvOrig[1], pSbrCom->sbrFIState[1].nEnv,
                           pEDState0->vSizeEnv, bs_amp_res);

/*
 * noise
 */
    sbriDequantNoiseUncouple(pSbrWS->bufNoiseOrig[0], pEDState1->bufNoiseQuant,
                             pSbrWS->bufNoiseOrig[1],
                             pEDState0->vSizeNoise[pSbrCom->sbrFIState[0].nNoiseEnv]);
  }

  return error;
}
/********************************************************************/

#endif //UMC_ENABLE_AAC_INT_AUDIO_DECODER

